#!/usr/bin/python
# -*- coding: utf-8 -*-
import threading
import logging
from socket import socket,SOL_SOCKET,SO_REUSEADDR
WAIT_MODE = -1
ROOM_MODE = 0
EXIT_MODE = -2
CMD_MAKEROOM = '@make'
CMD_ROOMLIST = '@room'
CMD_ENTERROOM = '@enter'
CMD_EXIT = '@exit'
MAX_ROOM = 40
ERROR_MSG = '명령 파싱 에러입니다. 다음과 같은 명령어를 사용해주세요. ( \'@enter -1\' , \'@room\' , \'@enter -1\' )\r\r\n'
user_list = []  #유저 스레드 보관
SERVER_INF = ("0.0.0.0",9876)
class ServerManager():
    room_list={}
    def __init__(self):
        pass
    def makeRoom(self,room_num):
        if not (room_num in self.room_list.keys()):
            self.room_list[room_num]=Room()         #방 생성
            return True
        else:
            return False

    def infoRoom(self):
        info = ''
        for index,i in enumerate(self.room_list.keys()):
            info += '['+str(index)+'] room number : '+str(i)+'\r\n'
        return info
    def exitRoom(self,user):
        if(user.room != -1):
            self.room_list[user.room].count -= 1
            return_str = (True,"방에서 나갔습니다. - " +str(self.room_list[user.room].count)+'명\m')
            if(self.room_list[user.room].count == 0):
                del self.room_list[user.room]
                user.socket.send('다른 방으로 이동 하였습니다. 마지막 사람이 나가서 방이 삭제되었습니다.\r\n')
            return return_str

    def enterRoom(self,user,room_number):
        if room_number in self.room_list.keys():
            if(self.room_list[room_number].count >= MAX_ROOM): #초과
                return (False,"[@alert 3]인원이 초과하였습니다.\r\n")
            if user.mode == ROOM_MODE:              #이미 방에 접속 중
                self.room_list[user.room].count -= 1#이전에 접속해 있던 방 인원 감소
                logging.debug(str(user.room)+"번 방에서 퇴장하셨습니다. 현재 "+str(self.room_list[user.room].count)+'명')
                if(self.room_list[user.room].count == 0):
                    user.socket.send('다른 방으로 이동 하였습니다. \r\n')


            self.room_list[room_number].count += 1  #접속한 방 인원 추가

            user.room = room_number                 #접속한 방 번호
            user.mode = ROOM_MODE                   #사용자 모드
            return (True,"방에 참가하였습니다. - " +str(self.room_list[room_number].count)+'명\r\n')
        else:
            return (False,"[@alert 2]방이 존재하지 않습니다")

class UserThread (threading.Thread) :
    manager = ServerManager()
    flag = True
    def __init__(self,user):
        threading.Thread.__init__(self)
        self.user = user
    def run(self):
        socket = self.user.socket       #user socket
        socket.send('[SSM]채팅방에 참여하신것을 환영합니다. made by Changwon, Python\r\n')
        while self.flag:
	    recvmsg = ''
            try:
	    	recvmsg = socket.recv(1024)
            except:
		recvmsg =''
	    if (recvmsg == '\r\n' or recvmsg =='\n' or recvmsg=='\r\n\r\n'):
               # logging.debug( 'enter 무시')
		pass
            else:
               # logging.debug( '입력 받음')
                if (not self.commandInWait(recvmsg)) and self.user.mode == ROOM_MODE :
                    self.commandInRoom(recvmsg)
            if self.user.mode == EXIT_MODE:

                break
            if len(recvmsg) == 0:
                if self.user.room != -1:
                    self.manager.room_list[self.user.room].count -= 1;   # count --
                    if(self.manager.room_list[self.user.room].count == 0):
                        del self.manager.room_list[self.user.room]
                user_list.remove(self)
                logging.info( 'client socket is dead')

                break;
        socket.close()

    def commandInWait(self,command):
        if(command != None):
            #return char index
            if command.find(CMD_MAKEROOM,0,len(CMD_MAKEROOM)) == 0: #방생성
                logging.debug('방 생성')
                try:
                    room_number = int((command[command.find('-')+1:]).strip())
                except Exception, msg:
                    logging.error(msg)
                    self.user.socket.send(ERROR_MSG)
                    return True #응답 처리

                logging.debug("[CMD_MAKEROOM]room id : "+str(room_number))
                if not self.manager.makeRoom(room_number):
                    self.user.socket.send('[@alert 1]이미 해당방이 존재합니다.\r\n')
                else :
                    self.user.socket.send('방 개설에 성공하였습니다.\r\n')


            elif command.find(CMD_ROOMLIST,0,len(CMD_ROOMLIST)) == 0:   #방리스트
                logging.debug('방 리스트')
                info = self.manager.infoRoom()
                if info != '':
                    self.user.socket.send(info)
                else:
                    self.user.socket.send("서버에 방이 존재하지 않습니다. 개설해 주세요\r\n")

            elif command.find(CMD_ENTERROOM,0,len(CMD_ENTERROOM)) == 0: #방접속
                try :
                    room_number = int((command[command.find('-')+1:]).strip())
                except Exception, msg:
                    logging.error(msg)
                    self.user.socket.send(ERROR_MSG)
                    return True

                logging.debug("[CMD_ENTERROOM]room id : "+str(room_number))
                result_tuple = self.manager.enterRoom(self.user,room_number)
                if not result_tuple[0]:
                    self.user.socket.send(result_tuple[1])  #방 입장 실패
                    logging.debug(self.user.username+'님 방 입장 실패\r\n')
                else:
                    self.user.socket.send(result_tuple[1])  #방 입장
                    logging.debug(self.user.username+'님 방 입장 성공\r\n')
                    logging.info(result_tuple[1])

            elif command.find(CMD_EXIT,0,len(CMD_EXIT)) == 0:   #접속 종료
                self.user.closeSocket()
                user_list.remove(self)
                msg = self.manager.exitRoom(self.user)
                self.user.mode = EXIT_MODE
                logging.info(msg[1])
                logging.info(str(self.user.username) +'이 서버에서 나갔습니다.\r\n')
                logging.info('현재 서버에 '+str(len(user_list))+'명의 사용자가 있습니다.\r\n')
            elif( len(command)!= 0 and command[0] == '@' and self.user.mode == ROOM_MODE):       #채팅방 일 때
                self.user.socket.send(ERROR_MSG + ' 다른 커맨드를 입력해주세요.(시작기호 @를 채팅에 사용할 수 없습니다.)\r\n')
            else :
                if(self.user.mode != ROOM_MODE):    #대기실 일 때
                    pass
                    self.user.socket.send(ERROR_MSG)
                return False    #명령어가 존재하지 않음
        else :
            return False    #명령어 존재하지 않음
        return True

    def commandInRoom(self,msg):
        self.multicastRoom(msg)

    def multicastRoom(self,msg):
        sameChannelPeople = (user_thread.user for user_thread in user_list if self.user.room == user_thread.user.room)
        for man in sameChannelPeople: #multi cast
            if (man != self.user):
                try:
                    man.socket.send('['+ self.user.username +']'+ msg+str('\r\n'))
                except Exception, msg:
                    logging.debug('상대의 소켓이 종료되었습니다.')
                    logging.debug(msg)
        pass

class Room():
    def __init__(self):
        self.count = 0
class User:
    socket = None
    room = -1
    mode = WAIT_MODE
    def __init__(self,socket,username):
        self.socket = socket
        self.username= username
    def closeSocket(self):
        if not self.socket:
            self.socket.close()

def chat_accept():
    try:
        s = socket()
        s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        s.bind(SERVER_INF)
        s.listen(1000)
        logging.info("[SSM]서버가 시작하였습니다. made by Changwon, Python")
        while 1:
            new_socket, addr = s.accept()
            logging.info(addr[0]+':'+str(addr[1])+'님 께서 접속하셨습니다.')
            user = User(new_socket,addr[0]+':'+str(addr[1]))
            user_thread = UserThread(user)
            user_list.append(user_thread)
            user_thread.start()
            logging.info('현재 서버에 '+str(len(user_list))+'명의 사용자가 있습니다.')
    except (KeyboardInterrupt, SystemExit, Exception), msg:
        logging.info(msg)
        logging.info('서버 종료')
        for i in user_list: # 소켓 종료
            i.flag=False
        s.close()

    finally:
        s.close()

__author__ = 'changwoncheo'
if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG)
    chat_accept()

